#include "fs/fs.h"
#include "fs/buf.h"
#include "fs/dir.h"
#include "fs/bitmap.h"
#include "fs/inode.h"
#include "fs/file.h"
#include "mem/vmem.h"
#include "proc/cpu.h"
#include "lib/print.h"
#include "fs/inode.h"

// 设备列表(读写接口)
dev_t devlist[N_DEV];

// ftable + 保护它的锁
#define N_FILE 32
file_t ftable[N_FILE];
spinlock_t lk_ftable;

// ftable初始化 + devlist初始化
void file_init()
{
    spinlock_init(&lk_ftable, "file_table");
    for (int i = 0; i < N_FILE; i++)
    {
        ftable[i].ref = 0; // 初始化引用计数
    }
    for (int i = 0; i < N_DEV; i++)
    {
        devlist[i].read = NULL;
        devlist[i].write = NULL;
    }
}

// alloc file_t in ftable
// 失败则panic
file_t* file_alloc()
{
    spinlock_acquire(&lk_ftable);
    for (int i = 0; i < N_FILE; i++)
    {
        if (ftable[i].ref == 0)
        {
            ftable[i].ref = 1; // 分配引用计数
            spinlock_release(&lk_ftable);
            return &ftable[i];
        }
    }
    spinlock_release(&lk_ftable);
    panic("file_alloc: no free file_t");
    return NULL;
}

// 创建设备文件(供proczero创建console)
file_t* file_create_dev(char* path, uint16 major, uint16 minor)
{
    inode_t* ip = inode_create(FT_DEVICE, major, minor);
    if (!ip)
        return NULL;

    file_t* f = file_alloc();
    f->type = FD_DEVICE;
    f->ip = ip;
    f->offset = 0;
    return f;
}

// 打开一个文件
file_t* file_open(char* path, uint32 open_mode)
{
    // 1. 根据路径查找对应的 inode
    inode_t* inode = NULL;
    if (open_mode & MODE_CREATE) { 
        // 如果打开模式中包含创建标志，则尝试创建 inode
        inode = path_create_inode(path, FT_FILE, 0, 0);
        if (inode == NULL) {
            printf("file_open: Failed to create inode for path %s\n", path);
            return NULL; // 创建失败，返回 NULL
        }
    }
    else {
        // 如果不包含创建标志，则只查找文件 inode
        inode = path_to_inode(path);
        if (inode == NULL) {
            printf("file_open: File not found for path %s\n", path);
            return NULL; // 文件不存在，返回 NULL
        }
    }
    file_t *file=file_alloc();
    // 3. 初始化 file_t 结构
    file->ip = inode;               // 绑定到文件的 inode
    file->offset = 0;               // 文件偏移量初始化为 0
    //file->ref = 1;                  // 引用计数已经由 file_alloc 初始化为 1

    // 设置可读可写权限
    file->readable = (open_mode & MODE_READ) != 0;
    file->writable = (open_mode & MODE_WRITE) != 0;

    inode_lock(inode); // 加锁

    // 判断文件类型
    if (inode->type == FT_DIR) {
        file->type = FD_DIR;
    } else if (inode->type == FT_FILE) {
        file->type = FD_FILE;
    } else if (inode->type == FT_DEVICE) {
        file->type = FD_DEVICE;
        file->major = inode->major;

        // 检查设备号是否有效
        if (inode->major < 0 || inode->major >= N_DEV || devlist[inode->major].read == NULL) {
            file_close(file); // 如果无效，关闭文件并释放资源
            inode_unlock(inode); // 解锁
            return NULL;
        }
    } else {
        file_close(file); // 如果类型未知，释放资源
        inode_unlock(inode); // 解锁
        return NULL;
    }
    inode_unlock(inode); // 解锁
    return file;
}

// 释放一个file
void file_close(file_t* file)
{
    spinlock_acquire(&lk_ftable);
    if (file->ref < 1)
        panic("file_close: invalid ref");

    if (--file->ref == 0)
    {
        if (file->type == FD_FILE || file->type == FD_DIR)
            inode_free(file->ip);
        file->ip = NULL;
        file->type = -1;
    }
    spinlock_release(&lk_ftable);
}

// 文件内容读取
// 返回读取到的字节数
uint32 file_read(file_t* file, uint32 len, uint64 dst, bool user)
{
    if (file->type == FD_DEVICE)
    {
        if (!devlist[file->ip->major].read)
            return -1;
        return devlist[file->ip->major].read(len, dst, user);
    }

    if (file->type == FD_FILE || file->type == FD_DIR)
    {
        inode_lock(file->ip);
        uint32 n = inode_read_data(file->ip, file->offset, len, (void*)dst, user);
        file->offset += n;
        inode_unlock(file->ip);
        return n;
    }
    return -1;
}

// 文件内容写入
// 返回写入的字节数
uint32 file_write(file_t* file, uint32 len, uint64 src, bool user)
{
    if (file->type == FD_DEVICE)
    {
        if (!devlist[file->ip->major].write)
            return -1;
        return devlist[file->ip->major].write(len, src, user);
    }

    if (file->type == FD_FILE)
    {
        inode_lock(file->ip);
        uint32 n = inode_write_data(file->ip, file->offset, len, (void*)src, user);
        file->offset += n;
        inode_unlock(file->ip);
        return n;
    }
    return -1;
}

// flags 可能取值
#define LSEEK_SET 0  // file->offset = offset
#define LSEEK_ADD 1  // file->offset += offset
#define LSEEK_SUB 2  // file->offset -= offset

// 修改file->offset (只针对FD_FILE类型的文件)
uint32 file_lseek(file_t* file, uint32 offset, int flags)
{
    if (file->type != FD_FILE)
        return -1;

    spinlock_acquire(&lk_ftable);
    switch (flags)
    {
    case LSEEK_SET:
        file->offset = offset;
        break;
    case LSEEK_ADD:
        file->offset += offset;
        break;
    case LSEEK_SUB:
        file->offset -= offset;
        break;
    default:
        spinlock_release(&lk_ftable);
        return -1;
    }
    spinlock_release(&lk_ftable);
    return file->offset;
}

// file->ref++ with lock
file_t* file_dup(file_t* file)
{
    spinlock_acquire(&lk_ftable);
    assert(file->ref > 0, "file_dup: ref");
    file->ref++;
    spinlock_release(&lk_ftable);
    return file;
}

// 获取文件状态
int file_stat(file_t* file, uint64 addr)
{
    file_state_t state;
    if(file->type == FD_FILE || file->type == FD_DIR)
    {
        inode_lock(file->ip);
        state.type = file->ip->type;
        state.inode_num = file->ip->inode_num;
        state.nlink = file->ip->nlink;
        state.size = file->ip->size;
        inode_unlock(file->ip);

        uvm_copyout(myproc()->pgtbl, addr, (uint64)&state, sizeof(file_state_t));
    }
    return -1;
}